Description
Release 1. The biggest gap and a data-loss safety net (select-all+type or Ctrl+U is currently unrecoverable). Add an undo stack in internal/editor capturing buffer+cursor snapshots (or reversible ops). Coalesce consecutive typing into one undo group; each structural op (newline, delete, paste, cut, delete-selection, kill-line, word-delete) is its own group. Bound history (e.g. 500 entries). Keys: Ctrl+Z undo, Ctrl+Y (and Ctrl+Shift+Z) redo. Restore cursor + scroll on undo/redo. Mark Dirty appropriately.
Acceptance Criteria
- #1 Ctrl+Z reverts the last edit group; Ctrl+Y / Ctrl+Shift+Z redoes
- #2 Typing is coalesced; structural ops are separate groups
- #3 Cursor and selection state restored on undo/redo
- #4 Redo stack cleared on a new edit; history bounded
Implementation Plan
- editor: snapshot struct {lines,cursor,scroll,anchor}; undo/redo stacks + lastKind; maxUndo=500.
- HandleKey wraps dispatch: classify key -> nav (break coalesce) | kindType (coalesce) | kindStructural (own group). Snapshot pre, run, commit if content changed; cap+clear redo.
- Undo()/Redo() swap snapshots, restore lines/cursor/scroll/anchor, Dirty=true. SetContent resets history. PushUndo() for app-driven paste/cut.
- app: Ctrl+Z->Undo, Ctrl+Y->Redo; PushUndo before paste/cut. (Ctrl+Shift+Z not sendable by terminals; Ctrl+Y is redo.)
- TDD: tests for coalesce, structural groups, cursor/selection restore, redo-cleared-on-edit, bounded history.
Implementation Notes
Implemented snapshot-based undo/redo in internal/editor/undo.go: bounded (500) undo/redo stacks; HandleKey wraps dispatch to record a checkpoint per edit group โ typing coalesces, structural ops (newline/delete/kill/word-delete/paste/cut) are separate groups; no-op keys skipped. Undo/Redo restore lines+cursor+scroll+selection; SetContent clears history; PushUndo() for app paste/cut. App: Ctrl+Z undo, Ctrl+Y redo (Ctrl+Shift+Z not distinguishable by terminals). Tests: internal/editor/undo_test.go + app routing test. Help text + README updated.